package cn.com.bonc.khjy.utils;

import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Set;

/**
 * @author BTW
 */
public class FtpUtil {
    private final static Log log = LogFactory.get();
    private FTPClient ftp;

    /**
     * @param addr     地址
     * @param port     端口号
     * @param username 用户名
     * @param password 密码
     * @return
     * @throws Exception
     */
    public boolean connect(String addr, int port, String username, String password) throws Exception {
        log.debug("创建FTP连接...");
        ftp = new FTPClient();
        int reply;
        log.debug("连接到：{}:{}", addr, port);
        if(!ftp.isConnected()){
            ftp.connect(addr, port);
        }
        log.debug("使用账号/密码[{}/{}]登陆...", username, password);
        ftp.login(username, password);
        ftp.setControlEncoding("UTF-8");
        ftp.setFileType(FTPClient.BINARY_FILE_TYPE);
        reply = ftp.getReplyCode();
        log.debug("FTP服务器返回状态码：{}", reply);
        if (!FTPReply.isPositiveCompletion(reply)) {
            log.error("连接FTP服务器失败！");
            ftp.disconnect();
            return false;
        }
        return true;
    }

    /**
     * @param file 上传的文件或文件夹
     * @param path 上传的文件的路径
     * @return void
     * @throws :(Error   note)
     * @throws Exception
     * @author
     * @title upload
     * @Description :
     * @time 2013 2013-11-27
     */
    public void upload( File file, String path ) throws Exception {

        System.out.println( " file.isDirectory() : " + file.isDirectory() );

        if ( file.isDirectory() ) {
            ftp.makeDirectory( file.getName() );
            ftp.changeWorkingDirectory( file.getName() );
            String[] files = file.list();
            for ( String file3 : files ) {
                File file1 = new File( file.getPath() + "\\" + file3 );
                if ( file1.isDirectory() ) {
                    upload( file1, path );
                    ftp.changeToParentDirectory();
                } else {
                    File file2 = new File( file.getPath() + "\\" + file3 );
                    FileInputStream input = new FileInputStream( file2 );
                    ftp.storeFile( file2.getName(), input );
                    input.close();
                }
            }
        } else {
            File file2 = new File( file.getPath() );

            System.out.println( " file.getPath() : " + file.getPath() + " | file2.getName() : " + file2.getName() );

            InputStream input = new FileInputStream( file2 );

            ftp.changeWorkingDirectory( path );
            ftp.storeFile( file2.getName(), input );

            input.close();  //关闭输入流
            ftp.logout();  //退出连接
        }
    }

    /**
     * @param remotePath FTP服务器访问目录
     * @param localPath  下载的文件本地路径
     * @return void
     * @throws :(Error   note)
     * @throws Exception
     * @author
     * @class cn.com.bonc.FtpUtil
     * @title download
     * @Description : FPT 下载文件方法
     * @time 2013 2013-11-27
     */
    public void download(String remotePath, String localPath) throws IOException {
        log.debug("开始从FTP服务器下载文件...");

        if (!ftp.changeWorkingDirectory(remotePath)) {
            log.warn("不能切换到远程目录{}", remotePath);
            return;
        }
        log.debug("改变远程目录地址：{}", remotePath);
        // 列出该目录下所有文件
        FTPFile[] fs = ftp.listFiles(remotePath, file -> !file.getName().startsWith("temp_") && !file.getName().endsWith(".COMPLETE") && file.isFile());
        // 遍历所有文件，找到指定的文件
        log.debug("发现{}个新日志文件", fs.length);
        Set<FTPFile> needResendFile = new HashSet<>(2);
        int retryCount = 0;
        while (retryCount <= 5) {
            boolean shouldContinue = sendFile(fs, needResendFile, localPath, retryCount);
            retryCount++;
            if (!shouldContinue) {
                break;
            }
        }
        log.debug("所有文件下载完毕！退出连接...");
        ftp.logout();  //退出连接
        ftp.disconnect();
    }

    private boolean sendFile(FTPFile[] file2Send, Set<FTPFile> need2ResendFiles, String localPath, int retryCount) {
        for (FTPFile ff : file2Send) {
            if (retryCount != 0) {
                log.debug("第{}次重发送开始", retryCount);
            }
            String fileName = ff.getName();
            Long fileSize = ff.getSize();
            if (fileSize == 0L) {
                log.debug("{} 大小为0，文件加入到文件异常队列，待本次循环结束后重新发送文件", fileName);
                need2ResendFiles.add(ff);
            } else {
                 log.debug("{}是完成的log文件，准备下载...,文件的大小为{}", fileName, ff.getSize());
                // 根据绝对路径初始化文件
                File localFile = new File(localPath + "/" + fileName);
                log.debug("确定下载文件到：{}", localFile.getAbsolutePath());
                // 输出流
                try (OutputStream is = new FileOutputStream(localFile)) {
                    // 下载文件
                     log.debug("开始下载...{}", fileName);

                    /*
                     *  ftp.retrieveFile(fileName, is); 这样的写法可能有 bug，可能会造成 ftp 文件拉取下来文件能正确创建，但大小为 0.
                     *  可参考 https://bbs.csdn.net/topics/300004876
                     *  具体结果需要等待测试
                     *  @date 2018/07/12
                     *  @author leucippus
                     */
                    // ftp.retrieveFile(fileName, is);
                    ftp.retrieveFile(new String(fileName.getBytes("GB2312"),"ISO-8859-1"), is);
                    // 把下载完成的文件从服务器上删除
                     log.debug("下载成功!删除文件{}...", fileName);
                    ftp.deleteFile(fileName);
                     log.debug("删除文件成功！");
                    need2ResendFiles.remove(ff);
                } catch (IOException e) {
                    log.error("下载文件失败", e);
                }
            }
        }
        return need2ResendFiles.size() != 0;
    }

    public void delete(String remotePath) throws IOException {
         log.debug("开始从FTP服务器删除文件...");
        if (!ftp.changeWorkingDirectory(remotePath)) {
            log.warn("不能切换到远程目录{}", remotePath);
            return;
        }
        // 列出该目录下所有文件
        FTPFile[] fs = ftp.listFiles(remotePath, file ->
        {
            // 如果文件的时间早于当前时间的一周前
            Calendar calendar = Calendar.getInstance();
            calendar.add(Calendar.DAY_OF_MONTH, -7);
            return file.getTimestamp().before(calendar) && file.getName().endsWith(".COMPLETE");
        });
        // 遍历所有文件，找到指定的文件
         log.debug("发现{}个文件", fs.length);
        for (FTPFile ff : fs) {
             log.debug("{}是完成的log文件，准备删除...", ff.getName());
            ftp.deleteFile(ff.getName());
        }
         log.debug("所有文件删除完毕！退出连接...");
        ftp.logout();  //退出连接
        ftp.disconnect();
    }

    public boolean uploadFile(String remotePath, String filePath, String targetFileName) {
        try (InputStream is = new FileInputStream( filePath )){
            ftp.setBufferSize( 1024 * 1024 );
             log.debug( "当前FTP工作目录：{}" , ftp.printWorkingDirectory() );
             log.debug( "远程请求指定存放目录：{}" , remotePath );

            if ( !ftp.printWorkingDirectory().contains( remotePath ) ) {
                if ( !ftp.changeWorkingDirectory( remotePath ) ) {
                    ftp.makeDirectory( remotePath );
                    ftp.changeWorkingDirectory( remotePath );
                }
            }

            long now = System.currentTimeMillis();
            //开始上传文件
             log.debug( "开始上传文件..." );

            boolean isSuccess = ftp.storeFile( targetFileName, is );
            if ( isSuccess ) {
                //上传成功
                long times = System.currentTimeMillis() - now;
                 log.debug( ">>>上传成功：文件：{},上传时间：{}秒", targetFileName, times / 1000 );
            } else {
                //上传失败
                 log.debug( ">>>上传失败：文件：{}", targetFileName );
            }
            return isSuccess;
        } catch ( IOException e ) {
            log.error( e, SystemMessageId.CMP0952, e.getMessage() );
            return false;
        } finally {
            try {
                ftp.logout();
                ftp.disconnect();
            } catch ( IOException e ) {
                log.error( e, SystemMessageId.CMP0952, e.getMessage() );
            }

        }
    }
}
